﻿using Microsoft.Practices.ESB.Adapter;
using Microsoft.Practices.ESB.Exception.Management;
using Microsoft.Practices.ESB.Itinerary;
using Microsoft.Practices.ESB.Itinerary.Services;
using Microsoft.Practices.ESB.Resolver;
using Microsoft.Practices.ESB.Transform;
using Microsoft.BizTalk.Component.Interop;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Streaming;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Xsl;
using System.Xml.XPath;

namespace BizTalk.ESB.Extensions.MessagingServices
{
    public class OptionalTransform : IMessagingService
    {
        private string _BizTalkSystemNamespace = @"http://schemas.microsoft.com/BizTalk/2003/system-properties";

        public IBaseMessage Execute(IPipelineContext context, IBaseMessage msg, string resolverString, IItineraryStep step)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            if (msg == null)
                throw new ArgumentNullException("msg");
            if (string.IsNullOrEmpty(resolverString))
                throw new ArgumentException("Resolver must be provided", "resolverString");
            try
            {
                ResolverInfo info = ResolverMgr.GetResolverInfo(ResolutionType.Endpoint, resolverString);
                if (!info.Success)
                    throw new RoutingException("Invalid Resolver", resolverString);

                // Resolve configuration for routing.
                Dictionary<string, string> resolverDictionary = ResolverMgr.Resolve(info, msg, context);

                var maptype = resolverDictionary["Resolver.TransformType"];

                if (String.IsNullOrEmpty(maptype))
                {
                    return msg;
                }
                else
                {

                    IBaseMessage outMsg;
                    outMsg = context.GetMessageFactory().CreateMessage();
                    outMsg.AddPart("Body", context.GetMessageFactory().CreateMessagePart(), true);
                    outMsg.Context = msg.Context;

                    if (msg.BodyPart != null)
                    {
                        var originalStrm = msg.BodyPart.GetOriginalDataStream();

                        if (originalStrm != null)
                        {
                            // Build Input Xml
                            XmlDocument inputMsg = new XmlDocument();
                            inputMsg.LoadXml(this.GetMessageContent(context, msg));

                            // Execute Transform
                            XmlDocument outputMsg = ExecuteTransform(inputMsg, maptype);
                            string outMessageType = outputMsg.DocumentElement.NamespaceURI + "#" + outputMsg.DocumentElement.LocalName;

                            // Build outgoing IBaseMessage
                            outMsg.Context.Promote("MessageType", this._BizTalkSystemNamespace, outMessageType);
                            var vStream = new VirtualStream();
                            outputMsg.Save(vStream);
                            vStream.Seek(0, SeekOrigin.Begin);
                            ReadOnlySeekableStream seekableStream = new ReadOnlySeekableStream(vStream);
                            outMsg.BodyPart.Data = seekableStream;
                            seekableStream.Seek(0, SeekOrigin.Begin);
                            outMsg.BodyPart.Data = seekableStream;
                            context.ResourceTracker.AddResource(seekableStream);
                        }
                    }

                    return outMsg;
                }
            }
            catch (System.Exception ex)
            {
                EventLogger.Write(MethodInfo.GetCurrentMethod(), ex);
                throw;
            }    
        }

        public string Name
        {
            get { return "BizTalk.ESB.Extensions.MessagingServices.OptionalTransform"; }
        }

        public bool ShouldAdvanceStep(IItineraryStep step, IBaseMessage msg)
        {
            return true;
        }

        public bool SupportsDisassemble
        {
            get { return false; }
        }

        #region Private Methods

        private XmlDocument ExecuteTransform(XmlDocument inputMessage, string MapTypeName)
        {
            if (String.IsNullOrEmpty(MapTypeName))
            {
                return inputMessage;
            }
            else
            {
                // Execute Map
                XmlDocument outputMessage = new XmlDocument();
                outputMessage.LoadXml(MapHelper.TransformMessage(inputMessage.OuterXml, MapTypeName));

                return outputMessage;
            }
        }

        private String GetMessageContent(IPipelineContext pContext, IBaseMessage message)
        {
            String msgData = string.Empty;
            Stream outStream = new VirtualStream();
            StreamWriter strmWriter = new StreamWriter(outStream);

            using (StreamReader sr = new
                          StreamReader(message.BodyPart.GetOriginalDataStream()))
            {
                msgData = sr.ReadToEnd();
                strmWriter.Flush(); // flush writer to ensure writing's done.
                strmWriter.Close(); // close stream since it won't be used
                sr.Close();
                return msgData;
            }
        }

        #endregion
    }
}
